/* 
 * File:   main.cpp
 * Author: James
 *
 * Created on November 5, 2010, 9:05 PM
 */
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
#include "Process.h"
#include "Scheduler.h"
#include "RTS.h"
#include "MFQS.h"

using namespace std;

void parseProcess(string str, int result[]) {



    int resulti = 0; // next index of result to fill
    int position = 0; // location in the string where the next number starts
    int i;
    for (i = 0; i <= str.length(); i++) {

        if (str[i] == '\t') { // hit a tab, add the number
            const char* substr = str.substr(position, i - position).c_str();
            result[resulti] = atoi(substr);
            resulti++;
            position = i + 1;
        } else if (i == str.length()) { // add last item
            const char* substr = str.substr(position, i - position).c_str();
            result[resulti] = atoi(substr);
        }

    }


}

bool getLastLine(const char *filename, string &lastLine) {

#define _LL_BUFFSIZE_ 2048

    lastLine.clear(); // regardless, zero out our return string
    if (!filename || !*filename) // if no file to work on, return false
        return false;

    char buff[_LL_BUFFSIZE_]; // our temporary input buffer

    ifstream is;
    is.open(filename);

    if (!is) // return false if couldn't open file
        return false;

    is.seekg(0, ios::end); // go to end of file
    int length = is.tellg(); // find out how large it is
    cout << "LENGTH: " << length << endl;
    is.seekg(length - min(length, _LL_BUFFSIZE_), ios::beg); // seek back from end a short ways

    // read in each line of the file until we're done
    buff[0] = 0;
    do {

        if (!isspace(buff[0]) && buff[0] != 0)
            lastLine = buff;


    } while (is.getline(buff, _LL_BUFFSIZE_));

    is.close();

    return true;
}


// http://www.codecodex.com/wiki/Heapsort
// modified to sort processes by arrival time

void heapsort(Process arr[], int N) {
    unsigned int n = N, i = n / 2, parent, child;
    Process t;

    for (;;) { /* Loops until arr is sorted */
        if (i > 0) { /* First stage - Sorting the heap */
            i--; /* Save its index to i */
            t = arr[i]; /* Save parent value to t */
        } else { /* Second stage - Extracting elements in-place */
            n--; /* Make the new heap smaller */
            if (n == 0) return; /* When the heap is empty, we are done */
            t = arr[n]; /* Save last value (it will be overwritten) */
            arr[n] = arr[0]; /* Save largest value at the end of arr */
        }

        parent = i; /* We will start pushing down t from parent */
        child = i * 2 + 1; /* parent's left child */

        /* Sift operation - pushing the value of t down the heap */
        while (child < n) {
            if (child + 1 < n && arr[child + 1].getArrive() > arr[child].getArrive()) {
                child++; /* Choose the largest child */
            }
            if (arr[child].getArrive() > t.getArrive()) { /* If any child is bigger than the parent */
                arr[parent] = arr[child]; /* Move the largest child up */
                parent = child; /* Move parent pointer to this child */
                child = parent * 2 + 1; /* Find the next child */
            } else {
                break; /* t's place is found */
            }
        }
        arr[parent] = t; /* We save t in the heap */
    }
}

/*
 * Run the program; Ask for algorithm/queue/input file data,
 * then execute the corresponding scheduler
 */
int main(int argc, char** argv) {

    // prompt user for algorithm

    cout << "Enter the scheduling algorithm (m = mfqs, r = rts, h = hws): " << endl;
    bool goodInput = false;

    string input = "";
    string inputQueues; //stores the number of queues to be used for mfqs
    string aging;
    string quantumInput;
    
    int numQueues; //used to store int value of
    int ageTime;
    int quantum;

    while (!goodInput) {

        getline(cin, input);


        // if good input, exit and set algorithm
        if ((input.compare("m") == 0)) {
            // prompt user for number of queues if MFQS, keep prompting until provided with valid quantity
			while(!goodInput){
				cout << endl << "Enter # of queues (3-5):" << endl;

				//validate that number of queues is between 3 and 5, as stated in class
				getline(cin, inputQueues);
				numQueues = atoi(inputQueues.c_str()); //convert input value to int for faster comparison
				if (numQueues >= 3 && numQueues <= 5) {
					goodInput = true;
				} else {
					cout << endl << "Invalid # of queues:" << endl;
				}
			}

			//reset while condition for obtaining quantum settings
			goodInput = false;

			while(!goodInput){
				//prompt user for quantum settings
				cout << endl << "Enter queue one quantum (each subsequent queue's quantum will be double the previous):" << endl;

				//validate that the quantum time is reasonable, > 0
				getline(cin, quantumInput);
				quantum = atoi(quantumInput.c_str());
				if(quantum > 0){
					goodInput = true;
				}
			}

			//reset while condition for obtaining aging settings
			goodInput = false;

			while(!goodInput){
				//prompt user for aging settings
				cout << endl << "Enter aging time:" << endl;

				//validate that the aging time is reasonable, > 0
				getline(cin, aging);
				ageTime = atoi(aging.c_str());
				if(ageTime > 0){
					goodInput = true;
				}
			}


		} else if (input.compare("r") == 0) {
			goodInput = true;

		} else if (input.compare("h") == 0) {
			goodInput = true;

		} else {
			// if bad input, print msg and loop to obtain good input
			cout << endl << "Invalid input." << endl;

		}

    }



    // get input file name
    goodInput = false;
    cout << endl << "Enter the name of the input file: " << endl;
    string inputFile = "";
    while (!goodInput) {

        getline(cin, inputFile);

        if (inputFile.compare("") != 0) {
            goodInput = true;
        }

    }

    cout << "Input Filename: " << inputFile << endl;
    //read input file, clean bad data and sort based on arrival time
    char* in = new char[inputFile.size() + 1]; // must be called with char*
    in[inputFile.size()] = 0;
    memcpy(in, inputFile.c_str(), inputFile.size());


    // get ID of the last process (will tell how big to make the array)
    string lline;
    getLastLine(in, lline);
    cout << "Last line of input file: " << lline << endl;

    int parsedLn[6];
    parseProcess(lline, parsedLn);
    int nProcs = parsedLn[0];
    cout << "Last ID: " << nProcs << endl;

    // allocate memory for all processes
    Process* processes;
    processes = new Process[nProcs + 1];
    int procI = 0;
    string line;
    ifstream myfile(in, ifstream::in);
    if (myfile.is_open()) {
        // remove first line
        getline(myfile, line);
        while (myfile.good()) {
            getline(myfile, line);
            //cout << line << endl;

            // store processes in an arr
            parseProcess(line, parsedLn); //fill array of process data
            processes[procI].initialize(parsedLn);
            procI++;
        }
        myfile.close();
    }
    else {
        cout << "Unable to open file";
        exit(1);
    }
    delete in;

    // sort process array (print statements prove that it's sorted)
    int i;
    cout << "Array :" << endl << "[ ";
    for (i = 0; i < nProcs; i++) {
        cout << processes[i].getID() << "|" << processes[i].getArrive() << endl;
    }
    cout << "]" << endl;
    heapsort(processes, nProcs + 1);

    cout << "Sorted Array :" << endl << "[ ";
    for (i = 0; i < nProcs; i++) {
        cout << processes[i].getID() << "|" << processes[i].getArrive() << endl;
    }
    cout << "]" << endl;


    // initialize queue data structures based on the algorithm
    // then execute
    Scheduler s;
    if (input.compare("m") == 0) {

        cout << "Algorithm: MFQS" << endl << "Queues: " << inputQueues << endl;
        MFQS m = MFQS(numQueues, ageTime, quantum, processes);
        cout << "MFQS Created"<<endl;
        m.scheduleProcesses();
        cout<<"MFQS Done"<< endl;
    } else if (input.compare("r") == 0) {

        cout << "Algorithm: RTS" << endl;
        s = RTS(processes, nProcs);
        s.run();

    } else if (input.compare("h") == 0) {

        cout << "Algorithm: HWS" << endl;

    }


    return 0;

}



